/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2007 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
aLONG with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

/**
 * Local Includes:
 **/
#include "commonheaders.h"
#include "ctrl.h"

/**
 * Macros:
 **/

LPTSTR EscapesToMultiline(LPTSTR str)
{  //converts "\\n" and "\\t" to "\r\n" and "\t" because a multi-line edit box can show them properly
	static const TCHAR escapes[]={
		'a','\a',
		'b','\b',
		'e',27,
		'f','\f',
		'r','\r',
		't','\t',
		'v','\v',
		'\\','\\'
	};

	DWORD i, cchStr = _tcslen(str);
	SHORT wExtra = 0;
	LPTSTR ptr;

	if(!str) return NULL;

	for(ptr = str; *ptr; ptr++) {
		switch(*ptr) {
			// unix linefeed -> windows linefeed
			case '\n':
				if(ptr == str || *(ptr - 1) != '\r') {
					if(wExtra <= 0) {
						i = ptr - str;
						wExtra = 8;
						if(!(str = (LPTSTR)mir_realloc(str, (cchStr + wExtra + 1) * sizeof(TCHAR))))
							return NULL;
						cchStr += wExtra;
						ptr = str + i;
						str[cchStr] = 0;
					}
					memmove(ptr + 1, ptr, (cchStr - (ptr - str) - 1) * sizeof(TCHAR));
					wExtra--;
					*ptr = '\r';
				}
				break;
			// mac linefeed -> windows linefeed
			case '\r':
				if(*(ptr + 1) != '\n') {
					if(wExtra <= 0) {
						i = ptr - str;
						wExtra = 8;
						if(!(str = (LPTSTR)mir_realloc(str, (cchStr + wExtra + 1) * sizeof(TCHAR))))
							return NULL;
						cchStr += wExtra;
						ptr = str + i;
						str[cchStr] = 0;
					}
					memmove(ptr + 2, ptr + 1, (cchStr - (ptr - str) - 2) * sizeof(TCHAR));
					wExtra--;
					ptr[1] = '\n';
				}
				break;
			// escapes -> windows linefeed
			case '\\':
				if(*(ptr + 1) == 'n') {
					*ptr++ = '\r';
					*ptr = '\n';
				}
				else 
				if(*(ptr + 1) == 't') {
					*ptr = '\t';
					memmove(ptr + 1, ptr + 2, (cchStr - (ptr - str) - 1) * sizeof(TCHAR));
					wExtra++;
				}
				break;
		}
	}
	return str;
}


/**
 * name:	Edit_Init
 * desc:	allocates the memory for an edit control's info structure if not done yet
 * param:	hEdit - windowhandle to control
 * return:	the info structure
 **/
LPECTRL	Edit_Init(HANDLE hContact, HWND hEdit)
{
	LPECTRL peCtrl = NULL;

	// allocate edit control's infostructure
	if(hEdit && !PtrIsValid(peCtrl = (LPECTRL)GetUserData(hEdit))) {
		HANDLE hContact = INVALID_HANDLE_VALUE;

		if(!(peCtrl = (LPECTRL)malloc(sizeof(ECTRL))))
			return DBVT_DELETED;
		peCtrl->nType = CTRL_EDIT;
		peCtrl->wFlags = 0;
		SetUserData(hEdit, peCtrl);
		if(!hContact) Edit_LimitText(hEdit, 1024);
	}
	return peCtrl;
}

/**
 * name:	Edit_SetValue
 * desc:	read a setting from database and set its value to the edit control
 * param:	hDlg	  - owning dialog box of edit control
 *			idEdit	  - edit controls identifier
 *			hContact  - handle to get setting from
 *			szModule  - module or NULL for base protocol to get the setting from
 *			szSetting - the setting to read
 *
 * return:	state of edit control (changed or unchanged)
 **/
BYTE Edit_SetValue(HWND hDlg, INT idEdit, HANDLE hContact, LPCSTR szModule, LPCSTR szBaseProto, LPCSTR szSetting)
{
	HWND hEdit = GetDlgItem(hDlg, idEdit);
	LPECTRL peCtrl = Edit_Init(hContact, hEdit);
	DBVARIANT dbv;
	LPCSTR szSubMod;

	if(!peCtrl) return 0;

	if(peCtrl->wFlags & CTRLF_CHANGED)
		return CTRLF_CHANGED;

	if(!szModule) szSubMod = USERINFO;
	else szSubMod = szModule;

	// read setting from contact's basic protocol
	peCtrl->wFlags &= ~(CTRLF_HASCUSTOM|CTRLF_HASPROTO|CTRLF_HASMETA);
	peCtrl->wFlags |= DBGetSettingCtrlUnicode(hContact, szModule, szSubMod, szBaseProto, szSetting, &dbv);
	
	EnableWindow(hEdit, 
		!hContact ||
		(peCtrl->wFlags & CTRLF_HASCUSTOM) || 
		!(peCtrl->wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)) ||
		!DBGetByte(SET_PROPSHEET_PCBIREADONLY, 0));

	peCtrl->valueType = dbv.type;
	switch(dbv.type) {
		case DBVT_BYTE:
			SetDlgItemInt(hDlg, idEdit, dbv.bVal, FALSE);
			break;

		case DBVT_WORD:
			SetDlgItemInt(hDlg, idEdit, dbv.wVal, FALSE);
			break;

		case DBVT_DWORD:
			SetDlgItemInt(hDlg, idEdit, dbv.dVal, FALSE);
			break;

		case DBVT_TCHAR:
			if(dbv.ptszVal) {
				SetWindowText(hEdit, dbv.ptszVal);
				break;
			}
		default:
			SetWindowText(hEdit, _T(""));
			break;
	}
	// changed flag may have been set by some message handler, so delete it
	peCtrl->wFlags &= ~CTRLF_CHANGED;
	DBFreeVariant(&dbv);
	return 0;
}

/**
 * name:	Edit_WriteValue
 * desc:	get value from edit control and write a setting to database
 * param:	hDlg		- owning dialog box of edit control
 *			idEdit		- edit controls identifier
 *			hContact	- handle to get setting from
 *			szModule	- module or NULL for base protocol to get the setting from
 *			szBaseProto - basic protocol for user
 *			szSetting	- the setting to read
 *
 * return 1 on failure 0 on success
 **/
BYTE Edit_WriteValue(HWND hDlg, INT idEdit, HANDLE hContact, LPCSTR szModule, LPCSTR szBaseProto, LPCSTR szSetting, BYTE bType)
{
	HWND hEdit = GetDlgItem(hDlg, idEdit);
	LPECTRL peCtrl;
	LPTSTR pszValue;

	if(!hEdit || !PtrIsValid(peCtrl = (LPECTRL)GetUserData(hEdit))) 
		return DBVT_DELETED;
	if(!hContact) szModule = szBaseProto;
	if(peCtrl->wFlags & CTRLF_CHANGED) {
		const DWORD cchText = GetWindowTextLength(hEdit);
		
		if(cchText > 0) {
			
			if(!(pszValue = (LPTSTR)alloca((cchText + 2) * sizeof(TCHAR))))
				return 0;
			pszValue[cchText + 1] = 0;
			
			GetWindowText(hEdit, pszValue, cchText + 1);

			switch(bType) {
				case DBVT_BYTE:
					if(DBWriteContactSettingByte(hContact, szModule, szSetting, (BYTE)_tcstol(pszValue, NULL, 10)))
						return 0;
					break;
				case DBVT_WORD:
					if(DBWriteContactSettingWord(hContact, szModule, szSetting, (WORD)_tcstol(pszValue, NULL, 10)))
						return 0;
					break;
				case DBVT_DWORD:
					if(DBWriteContactSettingDword(hContact, szModule, szSetting, (DWORD)_tcstol(pszValue, NULL, 10)))
						return 0;
					break;
				default:
					if(DBWriteContactSettingTString(hContact, szModule, szSetting, pszValue))
						return 0;
					break;
			}
			if(!hContact) {
				peCtrl->wFlags &= ~CTRLF_HASCUSTOM;
				peCtrl->wFlags |= CTRLF_HASPROTO;
			}
		}
		else {
			DBDeleteContactSetting(hContact, szModule, szSetting);
			peCtrl->wFlags &= ~CTRLF_HASCUSTOM;
		}
	}
	else
	if(hContact && !(peCtrl->wFlags & CTRLF_HASCUSTOM))
		DBDeleteContactSetting(hContact, szModule, szSetting);
	peCtrl->wFlags &= ~CTRLF_CHANGED;
	InvalidateRect(hEdit, NULL, TRUE);
	return bType;
}


/**
 * name:	Edit_ChangedLargeString
 * desc:	set changed flag for edit control and current propertysheetpage
 * param:	hDlg	- owning propertysheetpage dialog
 *			idEdit	- control identifier for the editbox
 *
 * return	nothing
 **/
INT Edit_Changed(HWND hDlg, INT idEdit, LPCSTR szModule, LPCSTR szSetting)
{
	HWND hEdit;
	LPECTRL peCtrl;
	HANDLE hContact;
	LPCSTR szBaseProto;
	INT	ccText;
	DBVARIANT dbv;
	LPSTR szText = NULL;
	BOOLEAN bChanged = FALSE;
	
	// dialog is locked, or invalid protocol? return
	if(PspIsLocked(hDlg)) return 0;
	if(!(hEdit = GetDlgItem(hDlg, idEdit))) return E_FAIL;
	// don't check if hEdit is wrong control 
	if(!PtrIsValid(peCtrl = (LPECTRL)GetUserData(hEdit)) || peCtrl->nType != CTRL_EDIT)
		return E_FAIL;

	if(!PSGetBaseProto(hDlg, szBaseProto))
		szBaseProto = NULL;

	if(!PSGetContact(hDlg, hContact))
		hContact = NULL;

	ccText = Edit_GetTextLength(hEdit);

	if((peCtrl->wFlags & CTRLF_CHANGED) && (!hContact || ccText > 0 || !(peCtrl->wFlags & CTRLF_HASCUSTOM)))
		return 1;
	
	// read value from db
	if((!(peCtrl->wFlags & CTRLF_HASCUSTOM) || DBGetSettingTS(hContact, szModule, szSetting, &dbv) || dbv.type == DBVT_DELETED) && szBaseProto) {
		DBGetSettingCtrlUnicode(hContact, NULL, szModule, szBaseProto, szSetting, &dbv);
	}
	
	// we may want to compare numbers or small ansi text strings?
	if(dbv.type <= DBVT_DWORD || (dbv.type == DBVT_ASCIIZ && ccText < MAXDATASIZE)) {
		CHAR szNumber[MAXDATASIZE];
		
		*szNumber = 0;

		if(ccText <= 0 || GetWindowTextA(hEdit, szNumber, sizeof(szNumber))) {
			switch(dbv.type) {
				case DBVT_DELETED:
					bChanged = *szNumber != 0;
					break;
				case DBVT_BYTE:
					bChanged = (BYTE)strtol(szNumber, NULL, 10) != dbv.bVal;
				case DBVT_WORD:
					bChanged = (WORD)strtol(szNumber, NULL, 10) != dbv.wVal;
					break;
				case DBVT_DWORD:
					bChanged = (DWORD)strtol(szNumber, NULL, 10) != dbv.dVal;
					break;
				case DBVT_ASCIIZ:
					bChanged = *szNumber == 0 || strcmp(szNumber, dbv.pszVal);
					break;
			}
		}
	}
	// text control contains unicode text
	else if(dbv.type == DBVT_WCHAR) {
		LPWSTR wszText = NULL;

		if(ccText <= 0) bChanged = *dbv.pwszVal != 0;
		else if(wszText = (LPWSTR)alloca((ccText + 1) * sizeof(WCHAR))) { 
			*wszText = 0;
			GetWindowTextW(hEdit, wszText, ccText + 1);
			bChanged = *wszText == 0 || wcscmp(wszText, dbv.pwszVal);
		}
	}
	// text control contains ansi text larger than MAXDATASIZE
	else if(dbv.type == DBVT_ASCIIZ) {
		LPSTR szText = NULL;

		if(ccText <= 0) bChanged = *dbv.pszVal != 0;
		else if(szText = (LPSTR)alloca(ccText + 1)) { 
			*szText = 0;
			GetWindowTextA(hEdit, szText, ccText + 1);
			bChanged = *szText == 0 || strcmp(szText, dbv.pszVal);
		}
	}

	DBFreeVariant(&dbv);
	// update the infostructure and redraw edit control if something changed
	if(bChanged) {
		SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
		if((peCtrl->wFlags & CTRLF_HASCUSTOM) && (peCtrl->wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)) && !ccText) {
			peCtrl->wFlags &= ~CTRLF_CHANGED;
			Edit_SetValue(hDlg, idEdit, hContact, NULL, szBaseProto, szSetting);
		}
		else {
			peCtrl->wFlags |= CTRLF_CHANGED;
			if(hContact) peCtrl->wFlags |= CTRLF_HASCUSTOM;
			InvalidateRect(hEdit, NULL, TRUE);
		}
	}
	return bChanged;
}

/**
 * name:	Edit_Destroy
 * desc:	destroys info structure associated with an edit control
 * param:	hDlg	- owning propertysheetpage dialog
 *			idEdit	- control identifier for the editbox
 *
 * return	nothing
 **/
VOID Edit_Destroy(HWND hDlg, INT idEdit)
{
	HWND hEdit = GetDlgItem(hDlg, idEdit);
	LPECTRL peCtrl;

	if(hEdit && PtrIsValid(peCtrl = (LPECTRL)GetUserData(hEdit))) {
		free(peCtrl);	
	}
}

/**
 * name:	Edit_OpenUrl
 * desc:	opens the url given in a editbox in the users default browser
 * param:	hDlg	- handle to propertypage
 *			idEdit	- control identifier for edit control
 **/
VOID Edit_OpenUrl(HWND hDlg, INT idEdit)
{
	HWND hEdit = GetDlgItem(hDlg, idEdit);
	INT lenUrl = 1 + Edit_GetTextLength(hEdit);
	LPSTR szUrl;
	
	if(lenUrl > 0 && (szUrl = (LPSTR)alloca(7 + lenUrl))) {
		if(GetWindowTextA(hEdit, szUrl, lenUrl) > 0)
			CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
	}
}

LRESULT Edit_LinkNotificationHandler(HWND hDlg, ENLINK* lnk)
{
	switch(lnk->msg) {
		case WM_SETCURSOR:
            SetCursor(LoadCursor(NULL, IDC_HAND));
            SetWindowLong(hDlg, DWL_MSGRESULT, TRUE);
            return TRUE;

        case WM_LBUTTONUP:
		{
			TEXTRANGEA	tr;

			// do not call function if user selected some chars of the url string
			SendMessage(lnk->nmhdr.hwndFrom, EM_EXGETSEL, NULL, (LPARAM) &tr.chrg);
			if(tr.chrg.cpMax != tr.chrg.cpMin)
				break;
			
			// retrieve the url string
			tr.chrg = lnk->chrg;
			tr.lpstrText = (LPSTR)_alloca(tr.chrg.cpMax - tr.chrg.cpMin + 1);
			if(!tr.lpstrText || SendMessageA(lnk->nmhdr.hwndFrom, EM_GETTEXTRANGE, NULL, (LPARAM)&tr) <= 0)
				break;

			CallService(MS_UTILS_OPENURL, 1, (LPARAM)tr.lpstrText);
		}
	}
	return FALSE;
}

/**
 * name:	RichEdit_GetTextLength
 * desc:	get length of text
 * param:	hRichEdit	  - window handle of the rich edit
 * return:	number of characters in the richedit
 **/
DWORD RichEdit_GetTextLength(HWND hRichEdit)
{
	GETTEXTLENGTHEX gtl;
	gtl.codepage = 1200;
	gtl.flags = GTL_NUMCHARS;
	return SendMessage(hRichEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, NULL);
}

/**
 * name:	RichEdit_SetValue
 * desc:	read a setting from database and set its value to the edit control
 * param:	hDlg	  - owning dialog box of edit control
 *			idEdit	  - edit controls identifier
 *			hContact  - handle to get setting from
 *			szModule  - module or NULL for base protocol to get the setting from
 *			szSetting - the setting to read
 *
 * return:	state of edit control (changed or unchanged)
 **/
BYTE RichEdit_SetValue(HWND hDlg, INT idEdit, HANDLE hContact, LPCSTR szModule, LPCSTR szBaseProto, LPCSTR szSetting)
{
	HWND hEdit = GetDlgItem(hDlg, idEdit);
	LPECTRL peCtrl = Edit_Init(hContact, hEdit);
	DBVARIANT dbv;
	LPCSTR szSubMod;

	if(!peCtrl) return 0;
	if(peCtrl->wFlags & CTRLF_CHANGED) return 1;

	if(!szModule) szSubMod = USERINFO;
	else szSubMod = szModule;

	// read setting from contact's basic protocol
	peCtrl->wFlags &= ~(CTRLF_HASCUSTOM|CTRLF_HASPROTO|CTRLF_HASMETA);
	//peCtrl->wFlags |= DBGetSettingCtrlUnicode(hContact, szModule, szSubMod, szBaseProto, szSetting, &dbv);

	if(!DBGetSettingUtf8(hContact, szModule, szSetting, &dbv)) {
		peCtrl->wFlags |= CTRLF_HASCUSTOM;
	}
	else
	if(!DBGetSettingUtf8(hContact, szBaseProto, szSetting, &dbv)) {
		peCtrl->wFlags |= CTRLF_HASPROTO;
	}

	EnableWindow(hEdit, 
		!hContact ||
		(peCtrl->wFlags & CTRLF_HASCUSTOM) || 
		!(peCtrl->wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)) ||
		!DBGetByte(SET_PROPSHEET_PCBIREADONLY, 0));

	if((peCtrl->valueType = dbv.type) == DBVT_UTF8) {
	
		if(dbv.pszVal) {
			SETTEXTEX st;
			st.flags = ST_DEFAULT;
			st.codepage = CP_UTF8;
			SendMessage(hEdit, EM_SETTEXTEX, (WPARAM)&st, (LPARAM)dbv.ptszVal);
		}
		else
			SetWindowText(hEdit, _T(""));
	}
	// changed flag may have been set by some message handler, so delete it
	peCtrl->wFlags &= ~CTRLF_CHANGED;
	DBFreeVariant(&dbv);
	return 0;
}

/**
 * name:	RichEdit_WriteValue
 * desc:	get value from edit control and write a setting to database
 * param:	hDlg		- owning dialog box of edit control
 *			idEdit		- edit controls identifier
 *			hContact	- handle to get setting from
 *			szModule	- module or NULL for base protocol to get the setting from
 *			szBaseProto - basic protocol for user
 *			szSetting	- the setting to read
 *
 * return 1 on failure 0 on success
 **/
VOID RichEdit_WriteValue(HWND hDlg, INT idEdit, HANDLE hContact, LPCSTR szModule, LPCSTR szBaseProto, LPCSTR szSetting)
{
	HWND hEdit = GetDlgItem(hDlg, idEdit);
	LPECTRL peCtrl;
	LPWSTR pszValue;
	GETTEXTEX gtx;
	DWORD cchText;

	if(hEdit && PtrIsValid(peCtrl = (LPECTRL)GetUserData(hEdit))) {
		if(!hContact) szModule = szBaseProto;
	
		if(peCtrl->wFlags & CTRLF_CHANGED) {
			if((cchText = RichEdit_GetTextLength(hEdit)) > 0 && (pszValue = (LPWSTR)alloca((cchText + 2) * sizeof(WCHAR)))) {
				pszValue[cchText + 1] = 0;
				gtx.cb = (cchText + 1) * sizeof(WCHAR);
				gtx.flags = GT_DEFAULT;
				gtx.codepage = 1200;
				gtx.lpDefaultChar = "?";

				if(SendMessage(hEdit, EM_GETTEXTEX, (WPARAM)&gtx, (LPARAM)pszValue) > 0) {
					// do utf8 encoding here to support database drivers which do not have unicode support
					LPSTR pszUtf8 = (LPSTR)WideCharToUtf8(pszValue, NULL);
					if(pszUtf8) {
						DBWriteContactSettingStringUtf(hContact, szModule, szSetting, pszUtf8);
						mir_free(pszUtf8);
					}
					goto end;
				}
			}
			DBDeleteContactSetting(hContact, szModule, szSetting);
			peCtrl->wFlags &= ~CTRLF_HASCUSTOM;
		}
		/*
		else
		if(hContact && !(peCtrl->wFlags & CTRLF_HASCUSTOM))
			DBDeleteContactSetting(hContact, szModule, szSetting);
		*/
	}
end:
	peCtrl->wFlags &= ~CTRLF_CHANGED;
	InvalidateRect(hEdit, NULL, TRUE);
}


/**
 * name:	RichEdit_Changed
 * desc:	set changed flag for edit control and current propertysheetpage
 * param:	hDlg	- owning propertysheetpage dialog
 *			idEdit	- control identifier for the editbox
 *
 * return	nothing
 **/
INT RichEdit_Changed(HWND hDlg, INT idEdit, LPCSTR szModule, LPCSTR szSetting)
{
	HWND hEdit;
	LPECTRL peCtrl;
	HANDLE hContact;
	LPCSTR szBaseProto;
	INT	ccText;
	DBVARIANT dbv;
	LPSTR szText = NULL;
	BOOLEAN bChanged = FALSE;
	
	// dialog is locked, or invalid protocol? return
	if(PspIsLocked(hDlg)) return 0;
	if(!(hEdit = GetDlgItem(hDlg, idEdit))) return E_FAIL;
	// don't check if hEdit is wrong control 
	if(!PtrIsValid(peCtrl = (LPECTRL)GetUserData(hEdit)) || peCtrl->nType != CTRL_EDIT)
		return E_FAIL;

	if(!PSGetBaseProto(hDlg, szBaseProto))
		szBaseProto = NULL;

	if(!PSGetContact(hDlg, hContact))
		hContact = NULL;

	ccText = RichEdit_GetTextLength(hEdit);
	if((peCtrl->wFlags & CTRLF_CHANGED) && (!hContact || ccText > 0 || !(peCtrl->wFlags & CTRLF_HASCUSTOM)))
		return 1;
	// read value from db
	if((!(peCtrl->wFlags & CTRLF_HASCUSTOM) || DBGetSettingUtf8(hContact, szModule, szSetting, &dbv) || dbv.type == DBVT_DELETED) && szBaseProto) {
		DBGetSettingUtf8(hContact, szBaseProto, szSetting, &dbv);
	}
	// text control contains unicode text
	if(dbv.type == DBVT_DELETED && ccText > 0)
		bChanged = TRUE;
	else
	if(dbv.type == DBVT_UTF8) {
		GETTEXTEX gtx;
		LPSTR szText = NULL;

		if(ccText <= 0) bChanged = *dbv.pwszVal != 0;
		else if(szText = (LPSTR)alloca((ccText + 2) * sizeof(CHAR))) { 
			szText[ccText + 1] = 0;
			gtx.cb = (ccText + 1) * sizeof(CHAR);
			gtx.codepage = CP_UTF8;
			gtx.lpDefaultChar = "?";
			gtx.flags = GT_DEFAULT;
			bChanged = (SendMessage(hEdit, EM_GETTEXTEX, (WPARAM)&gtx, (LPARAM)szText) == 0 || strcmp(szText, dbv.pszVal));
		}
	}
	DBFreeVariant(&dbv);
	// update the infostructure and redraw edit control if something changed
	if(bChanged) {
		SendMessage(GetParent(hDlg), PSM_CHANGED, 0, 0);
		if((peCtrl->wFlags & CTRLF_HASCUSTOM) && (peCtrl->wFlags & (CTRLF_HASPROTO|CTRLF_HASMETA)) && !ccText) {
			peCtrl->wFlags &= ~CTRLF_CHANGED;
			RichEdit_SetValue(hDlg, idEdit, hContact, NULL, szBaseProto, szSetting);
		}
		else {
			peCtrl->wFlags |= CTRLF_CHANGED;
			if(hContact) peCtrl->wFlags |= CTRLF_HASCUSTOM;
			InvalidateRect(hEdit, NULL, TRUE);
		}
	}
	return bChanged;
}

